﻿
namespace Webinator.Util
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Linq;
    using System.Runtime.InteropServices;
    using System.Text;
    using Enums;
    using ExtensionMethods;

    /// <summary>
    /// Window helper class.
    /// </summary>
    public static class WindowHelper
    {
        public static IntPtr GetDesktop()
        {
            return GetDesktopWindow();
        }

        public static void SendKey(IntPtr handle, int key)
        {
            PostMessage(handle, 0x100, (IntPtr)key, IntPtr.Zero);
            PostMessage(handle, 0x101, (IntPtr)key, IntPtr.Zero);
        }

        /// <summary>
        /// Sets the active window to the handle passed.
        /// </summary>
        /// <param name="handle">
        /// The handle.
        /// </param>
        /// <returns>
        /// True if the operation succeeded, False if it did not.
        /// </returns>
        public static bool SetActiveWindow(IntPtr handle)
        {
            IntPtr foregroundWindow = GetForegroundWindow();
            IntPtr dummy = IntPtr.Zero;

            uint foregroundThreadId = GetWindowThreadProcessId(foregroundWindow, dummy);
            uint thisThreadId = GetWindowThreadProcessId(handle, dummy);

            if (foregroundThreadId != thisThreadId)
            {
                if (AttachThreadInput(thisThreadId, foregroundThreadId, true))
                {
                    BringWindowToTop(handle);
                    SetForegroundWindow(handle);
                    AttachThreadInput(thisThreadId, foregroundThreadId, false);
                }
            }
            else
            {
                //IntPtr timeout = IntPtr.Zero;
                //SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, timeout, 0);
                //SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, dummy, SPIF_SENDCHANGE);
                BringWindowToTop(handle);
                SetForegroundWindow(handle);
                //SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, timeout, SPIF_SENDCHANGE);
            }

            return true;

            //ShowWindow(handle, (int)SW_RESTORE);
            //return SetForegroundWindow(handle);
        }

        /// <summary>
        /// Gets the window position with the specified handle.
        /// </summary>
        /// <param name="handle">The handle.</param>
        /// <returns>
        /// A <c>Rectangle</c>.
        /// </returns>
        public static Rectangle GetWindowPosition(IntPtr handle)
        {
            if (SetActiveWindow(handle))
            {
                RECT srcRect;
                if (!handle.Equals(IntPtr.Zero))
                {
                    if (GetWindowRect(handle, out srcRect))
                    {
                        int width = srcRect.Right - srcRect.Left;
                        int height = srcRect.Bottom - srcRect.Top;
                        return new Rectangle(srcRect.Left, srcRect.Top, width, height);
                    }
                }
            }

            return new Rectangle();
        }

        /// <summary>
        /// Sets the window position with the specified handle.
        /// </summary>
        /// <param name="handle">The handle.</param>
        /// <param name="size">The size of the window.</param>
        public static void SetWindowPosition(IntPtr handle, Size size)
        {
            if (SetActiveWindow(handle))
            {
                if (!handle.Equals(IntPtr.Zero))
                {
                    SetWindowPos(handle, IntPtr.Zero, 0, 0, size.Width, size.Height, 0x0002 & 0x0200 & 0x0040);
                }
            }
        }

        /// <summary>
        /// Maximizes the window.
        /// </summary>
        /// <param name="handle">
        /// The handle.
        /// </param>
        public static void MaximizeWindow(IntPtr handle)
        {
            if (SetActiveWindow(handle))
            {
                if (!handle.Equals(IntPtr.Zero))
                {
                    ShowWindow(handle, 3);
                }
            }
        }

        /// <summary>
        /// Gets the window state.
        /// </summary>
        /// <param name="handle">The handle.</param>
        /// <returns>
        /// The window state.
        /// </returns>
        public static WindowState GetWindowState(IntPtr handle)
        {
            WINDOWPLACEMENT placement = new WINDOWPLACEMENT();
            placement.length = Marshal.SizeOf(placement);
            GetWindowPlacement(handle, out placement);
            if (placement.showCmd == 1) return WindowState.Normal;
            if (placement.showCmd == 3) return WindowState.Maximized;
            if (placement.showCmd == 6) return WindowState.Minimized;
            return 0;
        }

        public enum WindowState
        {
            None = 0, Maximized, Minimized, Normal
        }

        public static void SetWindowTitle(IntPtr handle, string title)
        {
            SetWindowText(handle, title);
        }

        public static IEnumerable<IntPtr> GetDialogHandles(Comparison comparison, string title)
        {
            List<IntPtr> handles = new List<IntPtr>();

            foreach (IntPtr hWnd in GetDescendantWindows(IntPtr.Zero))
            {
                if (string.IsNullOrEmpty(title))
                {
                    if (WindowHelper.GetWindowClass(hWnd) == "#32770")
                    {
                        handles.Add(hWnd);
                    }
                }
                else
                {
                    var actualTitle = WindowHelper.GetWindowTitle(hWnd);
                    if (WindowHelper.GetWindowClass(hWnd) == "#32770" && actualTitle.StringMatch(comparison, title))
                    {
                        handles.Add(hWnd);
                    }
                }
            }

            return handles.ToList();
        }

        public static List<IntPtr> GetDescendantWindows(IntPtr parent)
        {
            List<IntPtr> result = new List<IntPtr>();
            GCHandle listHandle = GCHandle.Alloc(result);
            try
            {
                EnumWindowProc childProc = EnumWindow;
                EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
            }
            finally
            {
                if (listHandle.IsAllocated)
                    listHandle.Free();
            }

            return result;
        }

        public static string GetWindowClass(IntPtr hWnd)
        {
            int nRet;
            StringBuilder className = new StringBuilder(100);
            nRet = GetClassName(hWnd, className, className.Capacity);
            if (nRet != 0)
            {
                return className.ToString();
            }

            return string.Empty;
        }

        public static string GetWindowTitle(IntPtr hWnd)
        {
            int length = GetWindowTextLength(hWnd);
            StringBuilder sb = new StringBuilder(length + 1);
            GetWindowText(hWnd, sb, sb.Capacity);
            return sb.ToString();
        }

        private static bool EnumWindow(IntPtr handle, IntPtr pointer)
        {
            GCHandle gch = GCHandle.FromIntPtr(pointer);
            List<IntPtr> list = gch.Target as List<IntPtr>;
            if (list == null)
                throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");

            list.Add(handle);
            return true;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct RECT
        {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
        }

        private struct WINDOWPLACEMENT
        {
            public int length;
            public int flags;
            public int showCmd;
            public Point ptMinPosition;
            public Point ptMaxPosition;
            public Rectangle rcNormalPosition;
        }

        const UInt32 SW_HIDE = 0;
        const UInt32 SW_SHOWNORMAL = 1;
        const UInt32 SW_NORMAL = 1;
        const UInt32 SW_SHOWMINIMIZED = 2;
        const UInt32 SW_SHOWMAXIMIZED = 3;
        const UInt32 SW_MAXIMIZE = 3;
        const UInt32 SW_SHOWNOACTIVATE = 4;
        const UInt32 SW_SHOW = 5;
        const UInt32 SW_MINIMIZE = 6;
        const UInt32 SW_SHOWMINNOACTIVE = 7;
        const UInt32 SW_SHOWNA = 8;
        const UInt32 SW_RESTORE = 9;

        const uint SPI_GETFOREGROUNDLOCKTIMEOUT = 0x2000;
        const uint SPI_SETFOREGROUNDLOCKTIMEOUT = 0x2001;
        const int SPIF_SENDCHANGE = 0x2;

        [DllImportAttribute("User32.DLL")]
        private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);

        [DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool SetForegroundWindow(IntPtr hWnd);

        [DllImport("user32.dll", SetLastError = true)]
        private static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr lpdwProcessId);
        [DllImport("user32.dll")]
        private static extern IntPtr GetForegroundWindow();
        [DllImport("user32.dll")]
        private static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
        [DllImport("user32.dll")]
        static extern bool BringWindowToTop(IntPtr hWnd);

        [DllImport("User32.dll")]
        public static extern IntPtr GetParent(IntPtr hWnd); 

        [DllImport("user32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool GetWindowPlacement(IntPtr hWnd, out WINDOWPLACEMENT lpwndpl);

        private delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);

        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern bool SetWindowText(IntPtr hwnd, String lpString);

        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);

        [DllImport("user32")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr i);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern int GetWindowTextLength(IntPtr hWnd);

        [DllImport("user32.dll", SetLastError = false)]
        static extern IntPtr GetDesktopWindow();

        [DllImport("user32.dll")]
        private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

        [DllImport("user32.dll")]
        private static extern IntPtr PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

        [DllImport("user32.dll")]
        private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int width, int height, uint uFlags);
    }
}